class Compile{
  constructor(el,vm){
    this.el = this.isElementNode(el)?el:document.querySelector(el);
    this.vm = vm;
    // 编译挂载的对象
    if(this.el){
      // 1.将DOM移入到内存中
      let fragment = this.nodeToFragment(this.el);
      // 2.编译=》提取元素节点 v-model 和文本节点 {{}}
      this.compile(fragment);
      // 3.把编译好的fragment放回去页面
      this.el.appendChild(fragment);
    }
  }
  // 核心方法
  
  /**
   * 编译
   */
  compile(fragment){
    let childNodes = fragment.childNodes;
    // 将取到的节点转为可循环的数组，区分元素与节点，递归元素取到文本节点
    Array.from(childNodes).forEach(node=>{
      // 元素节点
      if(this.isElementNode(node)){
        this.compileElement(node)
        // 区分元素与节点，进行递归调用
        this.compile(node)
      }
      // 文本节点
      else{
        this.compileText(node)
      }
    })
  }
  // 对元素进行编译 v-model
  compileElement(node){
    let attrs = node.attributes;
    Array.from(attrs).forEach((attr) =>{
      // 判断属性名字是不是包含v-
      let attrName = attr.name;
      let expr = attr.value;
      if(this.isDirective(attrName)){
        let type = attrName.slice(2)
        // 取出对应的值放到节点中
        CompileUtil[type](node,this.vm,expr)
      }
    })
  }
  // 对文本节点进行编译 {{}}
  compileText(node){
    let expr = node.textContent;
    let reg = /\{\{([^}]+)\}\}/g;
    if(reg.test(expr)){
      // 获取绑定对应的值
      CompileUtil['text'](node,this.vm,expr)
    }
  }
  // 是否是指令
  isDirective(name){
    return name.includes('v-')
  }
  /**
   * 将DOM内容放入内存中
   * @param {Element} el 
   */
  nodeToFragment(el){
    // 创建一个代码碎片
    let fragment = document.createDocumentFragment();
    let firstChild;
    // 将DOM放入节点碎片中
    while(firstChild = el.firstChild){
      fragment.appendChild(firstChild)
    }
    return fragment;
  }
  // 辅助方法

  /**
   * 检测是否是是元素类型
   * @param {*} node 
   */
  isElementNode(node){
    return node.nodeType === 1
  }
}

CompileUtil = {
  // 文本处理
  text(node,vm,expr){
    let updateFn = this.updater.textUpdater;
    let value = this.getTextVal(vm,expr);
    expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{
      new Watcher(vm,arguments[1],(newValue)=>{
          //如果数据变化了，文本节点需要重新依赖的属性更新文本中的内容
          updateFn && updateFn(node,this.getTextVal(vm,expr));
      })
      return arguments[1];
  });
    updateFn && updateFn(node,value);
  },
  // 输入框处理
  model(node,vm,expr){
    let updateFn = this.updater.modelUpdater;
    // 数据监控，使用watch的callback
    new Watcher(vm,expr,(newValue)=>{
      console.log(newValue)
      // 当输入框值变化后会调用callback 将新的值传递回来
      updateFn && updateFn(node,this.getVal(vm,expr));
    });
    // 监控输入框变化
    node.addEventListener('input',(e)=>{
      let newValue = e.target.value;
      this.setVal(vm,expr,newValue)
    })
    // 将获取到的值，填入内存碎片中
    updateFn && updateFn(node,this.getVal(vm,expr));
  },
  setVal(vm,expr,value){
    expr = expr.split('.');
    return expr.reduce((prev,next,currentIndex)=>{
      if(currentIndex === expr.length-1){
        return prev[next] = value;
      }
      return prev[next];
    },vm.$data)
  },
  // 获取绑定的值 v-model="msg.a"
  getVal(vm,expr){
    expr = expr.split('.');
    return expr.reduce((prev,next)=>{
      return prev[next]
    },vm.$data)
  },
  getTextVal(vm,expr){
    return expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{
        return this.getVal(vm,arguments[1]);
    });
  },
  updater:{
    // 文本更新
    textUpdater(node,value){
      node.textContent = value;
    },
    // 绑定更新
    modelUpdater(node,value){
      node.value =value;
    }
  }
}